#include "mixp_skt_client.h"
#include "json/cJSON.h"

#include <sys/uio.h>

#define MAX_UUID_LEN (21)

static uint32_t create_sessionid(int, uint32_t, uint16_t);
static void on_recv(int, void *);
static void on_recv_json(mixp_skt_client_t *);
static void on_recv_bin(mixp_skt_client_t *);

mixp_skt_client_t *mixp_skt_client_new(mixp_skt_server_t *server, int fd, uint32_t ip, uint16_t port)
{
    mixp_skt_client_t *client = MALLOC(mixp_skt_client_t);

    client->sessionid = create_sessionid(fd, ip, port);
    client->fd = fd;
    client->addr = MALLOC(skt_addr_t);
    client->addr->ip = ip;
    client->addr->port = port;
    skt_addr_ntop(client->addr->ip_str, ip);

    client->server = server;
    client->wk_pid = PROCESS_ID_INVALID;

    client->req = MALLOC(mixp_packet_t);
    client->ack - MALLOC(mixp_packet_t);

    client->ev = gevent_create(fd, on_recv, NULL, NULL, (void *)client);
    if (-1 == gevent_add(server->evbase, client->ev))
    {
        logw("add skt client on recv event failed. errno: %d", errno);
        mixp_skt_client_free(client);
        return NULL;
    }

    mixp_skt_client_info(client);

    return client;
}

void mixp_skt_client_free(mixp_skt_client_t *client)
{
    char sessionid_str[9];
    char fd_str[9];

    snprintf(fd_str, sizeof(fd_str), "%08x", client->fd);
    snprintf(sessionid_str, sizeof(sessionid_str), "%08x", client->sessionid);

    mixp_skt_server_t *server = client->server;

    if (server)
    {
        hash_del(server->dict_fd2client, fd_str);
        hash_del(server->dict_uuid2fd, sessionid_str);
    }

    gevent_del(client->server->evbase, client->ev);
    gevent_destroy(client->ev);
    FREE(client->addr);
    FREE(client->req);
    FREE(client->ack);
    FREE(client);
}

void mixp_skt_client_info(mixp_skt_client_t *client)
{
    char *format = "client(%08x/%d,%s:%d)";
    snprintf(client->info, 64, format, client->sessionid, client->wk_pid, client->addr->ip_str, client->addr->port);
}

static uint32_t create_sessionid(int fd, uint32_t ip, uint16_t port)
{
    char uuid[MAX_UUID_LEN];
    uint32_t uuid_hash;

    snprintf(uuid, MAX_UUID_LEN, "%08x%08x%04x", fd, ip, port);
    uuid_hash = hash_gen32(uuid, sizeof(uuid));

    return uuid_hash;
}

static void on_disconnect(mixp_skt_client_t *client)
{
    logi("[ on_disconnect ] %s", client->info);

    mixp_skt_client_free(client);
}

static void on_recv(int fd, void *arg)
{
    mixp_skt_client_t *client = (mixp_skt_client_t *)arg;
    struct iovec *buf = MALLOC(struct iovec);
    int head_len = sizeof(mixp_packet_header_t);

    int ret = skt_recv(fd, &client->req->header, head_len);
    if (ret == 0 || ret != head_len)
    {
        // loge("failed recv packet header. ret: %d", ret);
        on_disconnect(client);
        goto err;
    }

    buf->iov_len = client->req->header.payload_len;
    buf->iov_base = calloc(1, buf->iov_len);
    client->req->payload = buf->iov_base;

    // logi("recv packet: ret: %d, head_len: %d, client->req->header.payload_len: %d", ret, head_len, buf->iov_len);

    ret = skt_recv(fd, buf->iov_base, buf->iov_len);
    // logi("recv packet payload. ret: %d, payload_len: %d, head_len: %d", ret, buf->iov_len, head_len);
    if (ret == 0 || ret != buf->iov_len)
    {
        loge("failed recv packet payload. ret: %d, payload_len: %d, head_len: %d", ret, client->req->header.payload_len, head_len);
        on_disconnect(client);
        goto err;
    }

    if (client->req->header.type == PACKET_CLIENT_JSON)
    {
        on_recv_json(client);
    }
    else if (client->req->header.type == PACKET_CLIENT_BINARY)
    {
        on_recv_bin(client);
    }
    else
    {
        loge("unknown packet type.");
    }
err:
    FREE(buf->iov_base);
    FREE(buf);
}

int mixp_skt_client_ack(mixp_skt_client_t *client, void *buf, size_t buf_len)
{
    logi("[ ack ] length: %d, to: %s", buf_len, client->info);
    return skt_send(client->fd, buf, buf_len);
}

static void on_recv_json(mixp_skt_client_t *client)
{
    cJSON *json = cJSON_Parse(client->req->payload);
    if (json)
    {
        logi("[ on_recv_json ]: %s, from: %s", cJSON_PrintUnformatted(json), client->info);
    }
    else
    {
        loge("[ on_recv_json ]: failed to decode json. len: %d, payload: %s, from: %s", client->req->header.payload_len, client->req->payload,
             client->info);
    }
    char *resp = "recved a json message.";
    mixp_skt_client_ack(client, resp, strlen(resp));
}
static void on_recv_bin(mixp_skt_client_t *client)
{
    logi("[ on_recv_bin ] length: %d, from: %s", client->req->header.payload_len, client->info);
    char *resp = "received a binary data.";
    mixp_skt_client_ack(client, resp, strlen(resp));
}